#include "cmdBuffer_t.h"
#include "error.h"
#include "../libcbgl32memory/libcbgl32memory.h"
#include <assert.h>
#include <memory.h>

/*
typedef struct cmdBlock
{
	cmd_t				commands[MAX_CMD_PER_BLOCK];
	size_t				available;
	struct cmdBlock*	next;
} cmdBlock_t;

typedef struct cmdBuffer
{
	cmdBlock_t*			first;
	size_t				cmdCount;
	paramBuffer_t*		params;
	blob_t*				blob;
} cmdBuffer_t;
*/


INLINE cmdBuffer_t* cmdBuffer_create()
{
	cmdBuffer_t* rv = cbgl_malloc( sizeof(cmdBuffer_t) );
	rv->blob = blob_create(0);
	rv->params = paramBuffer_create();
	rv->cmdCount = 0;
	rv->first = (cmdBlock_t*)cbgl_malloc( sizeof(cmdBlock_t) );
	rv->first->next = 0;
	rv->first->available = MAX_CMD_PER_BLOCK;
	return rv;
}

INLINE void cmdBuffer_addCmd(cmdBuffer_t* buf, void* pFunc, void* args, size_t argsSize)
{
	cmdBlock_t* current;

	// 1: find available cmd block
	assert(buf);

	current = buf->first;
	while(!current->available && current->next)
	{
		current = current->next;
	}

	if(!current->available)
	{
#ifdef _DEBUG
		message("Command block allocation!");
#endif
		current->next = (cmdBlock_t*)cbgl_malloc( sizeof(cmdBlock_t) );
		current->next->next = 0;
		current->next->available = MAX_CMD_PER_BLOCK;
		current = current->next;
	}
	// 2: init cmd
	cmd_init(current->commands + (MAX_CMD_PER_BLOCK-current->available), pFunc, args, argsSize);
	current->available--;
	buf->cmdCount++;
}

INLINE void cmdBuffer_Enumerate(cmdBuffer_t* buf, pCmdEnumProc pEnum, void* pUserPtr)
{
	cmdBlock_t* current;
	size_t it, sz;
#ifdef _DEBUG
	size_t cmdEnumerated = 0;
#endif

	assert(buf);

	current = buf->first;
	while(current)
	{
		sz = MAX_CMD_PER_BLOCK-current->available;
		for(it = 0; it < sz; ++it)
		{
#ifdef _DEBUG
			cmdEnumerated++;
#endif
			pEnum(current->commands + it, pUserPtr);
		}

		current = current->next;
	}

	assert(cmdEnumerated == buf->cmdCount);
}

INLINE void cmdBuffer_execute(cmdBuffer_t* buf)
{
	cmdBlock_t* current;
	size_t it, sz;
	void* savedESP;
	cmd_t* cmd;
	size_t cmdSize;
	void* func;

#ifdef _DEBUG
	size_t cmdEnumerated = 0;
#endif

	assert(buf);

	current = buf->first;
	while(current)
	{
		sz = MAX_CMD_PER_BLOCK-current->available;
		for(it = 0; it < sz; ++it)
		{
#ifdef _DEBUG
			cmdEnumerated++;
#endif
			cmd = current->commands + it;
			cmdSize = cmd->argsSize;
			func = cmd->pFunc;

			__asm
			{
				mov		savedESP, esp;
				sub		esp, cmdSize;
			}

			memcpy((byte*)savedESP - cmdSize, cmd->pArgs, cmdSize);

			__asm
			{
				call func;
			}
			current->available++;			
		}
		current = current->next;
	}

	assert(cmdEnumerated == buf->cmdCount);
	buf->cmdCount = 0;
	paramBuffer_reset(buf->params);

	if(buf->blob->available < MAX_BDATA_PER_BLOB)
	{
		blob_destroy(buf->blob);
		buf->blob = blob_create(0);
	}
}

INLINE void cmdBuffer_destroy(cmdBuffer_t* buf)
{
	cmdBlock_t* tmp, *current;

	assert(buf);

	blob_destroy(buf->blob);
	paramBuffer_destroy(buf->params);
	current = buf->first;
	while(current)
	{
		tmp = current;
		current = current->next;
		cbgl_free(tmp);
	}

	cbgl_free(buf);
}